package uva;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import rice.p2p.commonapi.Node;
import rice.p2p.commonapi.Id;
import rice.pastry.NodeHandle;
import rice.pastry.PastryNode;
import rice.pastry.routing.RouteSet;
import rice.pastry.routing.RoutingTable;

public class GraphViz {

	private PrintWriter out;
	private FileWriter outFile;
	private ArrayList<GraphVizEdge> edges;

	public GraphViz(String pathToFile) throws IOException {
		outFile = new FileWriter(pathToFile);
		edges = new ArrayList<GraphVizEdge>();
		out = new PrintWriter(outFile);
	}

	public ArrayList<GraphVizEdge> GetEdges() {
		return edges;
	}

	public void addEdges(Node node) {

		RoutingTable mytable = ((PastryNode) node).getRoutingTable();
		Id id = node.getId();

		for (int i = 0; i < mytable.numRows(); i++) {
			RouteSet[] row = mytable.getRow(i);

			for (int j = 0; j < mytable.numColumns(); j++) {
				RouteSet column = row[j];

				if (column != null) {
					String destination = column.get(0).getId().toStringFull();
					if (!id.toStringFull().equals(destination)) {

						GraphVizEdge edge = new GraphVizEdge(id.toStringFull(), destination);

						int index = checkIfReversePresent(edge);
						if (index >= 0) {
							edges.get(index).setIsBiDirectional(true);
						}
						else {
							edges.add(edge);
						}
					}
				}

				// "5th Edition" -> "6th Edition"[label="5"];
				// "6th Edition" -> "5th Edition"[label="1"];
				// "5th Edition" -> "PWB 1.0";
				// "5th Edition"[color=red];
			}
		}
	}

	public int checkIfReversePresent(GraphVizEdge edgeToCheck) {
		int i = 0;
		for (GraphVizEdge edge : edges) {
			String myDestination = edge.getDestination();
			String mySource = edge.getSource();
			String otherSource = edgeToCheck.getSource();
			String otherDestination = edgeToCheck.getDestination();

			if (myDestination.equals(otherSource) && mySource.equals(otherDestination)) {
				return i;
			}
			i++;
		}
		return -1;
	}

	public boolean checkIfEdgeIsMalicious(GraphVizEdge edge) {
		MaliciousNodes maliciousNodes = MaliciousNodes.getInstance();

		for (NodeHandle knownMaliciousNodeHandle : maliciousNodes) {
			String maliciousId = knownMaliciousNodeHandle.getId().toStringFull();
			
			if(maliciousNodes.isPresent(edge.getDestination()) && maliciousNodes.isPresent(edge.getSource())){
				return false;
			}
			else if (edge.getDestination().contains(maliciousId)) {
				return true;
			}
			else if (edge.getIsBiDirectional() && edge.getSource().contains(maliciousId)) {
				return true;
			}

		}
		return false;
	}

	public int hasMaliciousEdge(GraphVizEdge edgeToCheck) {

		for (int i = 0; i < edges.size(); i++) {
			GraphVizEdge currentEdge = edges.get(i);

			boolean sourceEdgesAreEqual = edgeToCheck.getSource().equals(currentEdge.getSource());
			boolean destinationEdgesAreEqual = edgeToCheck.getDestination().equals(currentEdge.getDestination());
			// boolean edgeIsMalicious = checkIfEdgeIsMalicious(edgeToCheck);
			boolean edgeToCheckDestinationEqualsCurrentSource = edgeToCheck.getDestination().equals(currentEdge.getSource());
			boolean edgeToCheckSourceEqualsCurrentDestination = edgeToCheck.getSource().equals(currentEdge.getDestination());

			if (destinationEdgesAreEqual && sourceEdgesAreEqual) {
				return i;
			}
			else {

				if (edgeToCheckDestinationEqualsCurrentSource && edgeToCheckSourceEqualsCurrentDestination && edgeToCheck.getIsBiDirectional().equals(currentEdge.getIsBiDirectional()) && edgeToCheck.getIsBiDirectional()) {
					return i;
				}
				else if (!edgeToCheck.getIsBiDirectional() && currentEdge.getIsBiDirectional() && (edgeToCheckSourceEqualsCurrentDestination || destinationEdgesAreEqual)) {
					return i;
				}
				else if (edgeToCheck.getIsBiDirectional() && !currentEdge.getIsBiDirectional() && edgeToCheckSourceEqualsCurrentDestination) {
					return i;
				}
			}
		}

		return -1;
	}

	public int calculatePrintMaliciousNodes() {

		int maliciousEdgeCount = 0;
		int totalEdgeCount = 0;

		for (GraphVizEdge edge : edges) {
			totalEdgeCount++;
			boolean isMalicious = checkIfEdgeIsMalicious(edge);

			if (isMalicious) {
				maliciousEdgeCount++;
			}
		}

		System.out.println("graph has " + maliciousEdgeCount + " malicious edges, out of " + totalEdgeCount + " total edges");

		return maliciousEdgeCount;
	}

	public void drawGraph() throws IOException {

		int maliciousEdgeCount = 0;
		int totalEdgeCount = 0;
		out.println("digraph unix {");
		out.println("size=\"36,36\";");
		out.println("node [color=lightblue2, style=filled];");

		for (GraphVizEdge edge : edges) {
			totalEdgeCount++;
			boolean isMalicious = checkIfEdgeIsMalicious(edge);

			if (!edge.getIsBiDirectional()) {
				if (isMalicious) {
					out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\" [color=\"red\"];");
					maliciousEdgeCount++;
				}
				else {
					out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\";");
				}
			}
			else {
				if (isMalicious) {
					out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\" [dir=\"both\", color=\"red\"];");
					maliciousEdgeCount++;
				}
				else {
					out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\" [dir=\"both\"];");
				}
			}
		}

		// "5th Edition" -> "6th Edition"[label="5"];
		// "6th Edition" -> "5th Edition"[label="1"];
		// "5th Edition" -> "PWB 1.0";
		// "5th Edition"[color=red];

		out.println("}");
		out.close();

		outFile.close();
		System.out.println("done creating graph with " + maliciousEdgeCount + " malicious edges, out of " + totalEdgeCount + " total edges");
	}

	public void printAllMaliciousEdges() {

		for (GraphVizEdge edge : edges) {
			boolean isMalicious = checkIfEdgeIsMalicious(edge);

			if (!edge.getIsBiDirectional()) {
				if (isMalicious) {
					System.out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\";");
				}
			}
			else {
				if (isMalicious) {
					System.out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\" -- bidirectional;");
				}
			}
		}
	}

	public void printAllEdges() {
		for (GraphVizEdge edge : edges) {
			if (!edge.getIsBiDirectional()) {

				System.out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\";");

			}
			else {
				System.out.println("\"" + edge.getSource() + "\" -> \"" + edge.getDestination() + "\" -- bidirectional;");
			}
		}
	}

}
